home *** CD-ROM | disk | FTP | other *** search
-
- /*DungeonE – prototype game example for the Mac Game book*/
- /*By Ingemar Ragnemalm 1995*/
- /**/
- /*This game is somewhat similar to MemoryGame, in that it uses a grid, represented*/
- /*by an array. The game is a typical (though extremely simplified) dungeon-digging game,*/
- /*where the objective is to collect treasures and fight monsters.*/
-
- /*Representation:*/
- /*The array tileArr holds nearly all information we need. telling what is in each space in the grid.*/
- /*Monsters and treasures are only represented this way. In a real game, you may wish to*/
- /*keep a list of all monster and treasure positions, to avoid scanning for them and to keep more*/
- /*information about each.*/
- /*The game also keeps an array that tells what spaces are known to the player (tilesKnown), and*/
- /*the player position (playerPosition), so we don't have to scan for it all the time.*/
- /*The combat system is extremely simple. If you try moving to an enemy, you have 60% chance to*/
- /*hit it and kill it. If an enemy tries to move to you, it has 50% chance to hit, reducing your hit points*/
- /*by one.*/
- /**/
- /*The program ends when we select "Quit".*/
-
- /* This version is based on Dungeon 2, and adds the following features:*/
- /* - Game entity list, describing all game entities in a list rather than*/
- /* as parts of an array. */
-
-
- #include <Sound.h>
-
- /*Size of the array*/
- #define kArraySizeH 15
- #define kArraySizeV 12
-
- /*Size of the tiles*/
- #define kTileSizeH 32
- #define kTileSizeV 32
-
- /*Menu ids*/
- #define appleID 127
- #define fileID 128
-
- /* A macro for taking the abs of a value */
- #define abs(x) (x>0?x:-x)
-
- /* All the possible states of a tile (space in the dungeon) */
- //typedef enum {empty, wall, player, enemy, tempEnemy, gold, exitPos} TileState;
- typedef enum {empty, wall, exitPos} TileState;
-
- /* The window pointer */
- WindowPtr myWindow;
-
- /* Arrays describing the dungeon */
-
- /* What tiles have we seen? */
- Boolean tileKnown[kArraySizeH][kArraySizeV];
- /* What does each tile contain? */
- /* tileArray now only holds static objects */
- TileState tileArray[kArraySizeH][kArraySizeV];
-
- /*Variables describing the player:*/
- //Point playerPosition; -> player->position
- short playerHitPoints; // NOT -> player->value
-
- /* A boolean telling if we should quit yet or not */
- Boolean gDone = false;
-
- /*Pictures*/
- PicHandle floorTile;
- PicHandle wallTile;
- PicHandle exitTile;
- PicHandle playerTile;
- PicHandle enemyTile;
- PicHandle goldTile;
-
- /*All 8 directions as vectors*/
- Point directionTable[8] = { { 0, 1 },
- {-1, 1 },
- {-1, 0 },
- {-1,-1 },
- { 0,-1 },
- { 1,-1 },
- { 1, 0 },
- { 1, 1 }};
-
-
-
- /************************************************/
- /* Types and variables for game entity handling */
- /************************************************/
-
- /* All the possible kinds of game entities */
- typedef enum {playerEntity, enemyEntity, goldEntity} EntityType;
-
- /*Variables describing a game entity*/
- typedef struct GameEntityRecord {
- EntityType kind; /* What kind of entity? */
- Point position; /* Where in the grid? */
- short value; /* Hit points or point value */
- struct GameEntityRecord *previous, *next; /* Next and previous entity in the list */
- } GameEntityRecord;
-
- typedef GameEntityRecord *GameEntityPtr; /* Define a handle type to it */
-
-
- /* A global pointer is the root of the entity list */
- GameEntityPtr gEntityList = nil;
-
- /* The player entity must be easily accessible */
- GameEntityPtr player;
-
- /* tileArray now only holds static objects */
- /* What entity is in each tile? */
- GameEntityPtr entityArray[kArraySizeH][kArraySizeV];
-
- /*** End of game entity handling types and variables ***/
-
-
-
-
- /* A function that generates a value in the interval 0..range-1 */
-
- static short Rand(short range)
- {
- return (Random () & 0x7fff) % range;
- }; /*Rand*/
-
-
- /* Draw a tile */
-
- static void DrawTile(short h, short v)
- {
- Rect tileRectangle;
-
- SetRect(&tileRectangle, h * kTileSizeH, v * kTileSizeV, (h + 1) * kTileSizeH, (v + 1) * kTileSizeV);
- if ( tileKnown[h][v] )
- {
- switch ( tileArray[h][v] )
- {
- case empty:
- DrawPicture(floorTile, &tileRectangle); break;
- case wall:
- DrawPicture(wallTile, &tileRectangle); break;
- case exitPos:
- DrawPicture(exitTile, &tileRectangle); break;
- default:
- PaintRect(&tileRectangle);
- }
- if (entityArray[h][v] != nil)
- switch (entityArray[h][v]->kind)
- {
- case playerEntity:
- DrawPicture(playerTile, &tileRectangle); break;
- case enemyEntity:
- DrawPicture(enemyTile, &tileRectangle); break;
- case goldEntity:
- DrawPicture(goldTile, &tileRectangle); break;
- default:
- ;
- }
- }
- else
- PaintRect(&tileRectangle);
- } /*DrawTile*/
-
-
-
- /*************************************/
- /* Routines for game entity handling */
- /*************************************/
-
- /* NewEntity allocates space for a new entity and puts it in the entity list */
- static GameEntityPtr NewEntity()
- {
- GameEntityPtr newEntity;
-
- newEntity = (GameEntityPtr) NewPtr(sizeof(GameEntityRecord));
- if (newEntity == nil) return nil;
- if (gEntityList != nil)
- {
- gEntityList->previous = newEntity;
- }
- newEntity->next = gEntityList;
- newEntity->previous = nil;
- gEntityList = newEntity;
- return newEntity;
- } /*NewEntity*/
-
- /* DisposeEntity removes an entity from the list and disposes it. */
- static void DisposeEntity(GameEntityPtr doomedEntity)
- {
- if (doomedEntity == nil) return;
- if (doomedEntity->next != nil)
- doomedEntity->next->previous = doomedEntity->previous;
- if (doomedEntity->previous != nil)
- doomedEntity->previous->next = doomedEntity->next;
- if (doomedEntity == gEntityList)
- gEntityList = doomedEntity->next;
- DisposePtr((Ptr)doomedEntity);
- } /*DisposeEntity*/
-
-
- /* MakeEntity builds an entity with the given values and puts it into the entityArray */
- static GameEntityPtr MakeEntity(EntityType eType, short hPos, short vPos, short value)
- {
- GameEntityPtr newEntity;
-
- newEntity = NewEntity();
- if (newEntity == nil) return nil;
- newEntity->position.h = hPos;
- newEntity->position.v = vPos;
- newEntity->kind = eType;
- newEntity->value = value;
- /* Border checking here would be good for safety */
- entityArray[newEntity->position.h][newEntity->position.v] = newEntity;
- DrawTile(newEntity->position.h, newEntity->position.v);
- return newEntity;
- } /*MakeEntity*/
-
- /* KillEntity disposes an entity, removes it from EntityArray and erases it */
- static void KillEntity(GameEntityPtr doomedEntity)
- {
- if (doomedEntity == nil) return;
- if (entityArray[doomedEntity->position.h][doomedEntity->position.v] == doomedEntity)
- entityArray[doomedEntity->position.h][doomedEntity->position.v] = nil;
- DrawTile(doomedEntity->position.h, doomedEntity->position.v);
- DisposeEntity(doomedEntity);
- } /*KillEntity*/
-
- /*** End of game entity handling routines ***/
-
-
-
- /*** Score handling and display ***/
-
- /*Number of extra pixels below game graphics*/
- #define kScoreFieldHeight 15
- /*Base line for text, how far from bottom of window*/
- #define kScoreBaseLine 3
-
- /*The score is a global*/
- long gGold, gExperience;
-
-
- static void DrawScore()
- {
- Str255 tempString;
- Rect blankRect;
-
- SetPort(myWindow);
-
- blankRect = myWindow->portRect;
- blankRect.top = myWindow->portRect.bottom - kScoreFieldHeight;
- EraseRect(&blankRect);
-
- MoveTo(myWindow->portRect.right * 1 / 5, myWindow->portRect.bottom - kScoreBaseLine);
- DrawString("\pGold: ");
- NumToString(gGold, tempString);
- DrawString(tempString);
-
- MoveTo(myWindow->portRect.right * 2 / 5, myWindow->portRect.bottom - kScoreBaseLine);
- DrawString("\pExperience: ");
- NumToString(gExperience, tempString);
- DrawString(tempString);
-
- MoveTo(myWindow->portRect.right * 3 / 5, myWindow->portRect.bottom - kScoreBaseLine);
- DrawString("\pHit points: ");
- NumToString(playerHitPoints, tempString);
- DrawString(tempString);
-
- } /*DrawScore*/
-
-
- static void AddScore(long addToGold, long addToExperience)
- {
- gGold += addToGold;
- gExperience += addToExperience;
- DrawScore();
- } /*AddScore*/
-
-
- /* Set all tiles around the player to be known, and draw them if they were not known before. */
-
- static void ShowAroundPlayer()
- {
- short h, v;
-
- for ( h = player->position.h - 1 ; h <= player->position.h + 1 ; h++)
- for ( v = player->position.v - 1 ; v <= player->position.v + 1 ; v++)
- if ( ! tileKnown[h][v] )
- {
- tileKnown[h][v] = true;
- DrawTile(h, v);
- }
- } /*ShowAroundPlayer*/
-
-
- /* The level generation routine */
-
- static void CreateLevel()
- {
- Rect roomRect[10];
- short room;
- short height, width;
- short h, v, h1, v1, h2, v2;
- short numRooms;
- short numTreasures, numMonsters;
- short i;
-
- /*For each tile position, we set the initial state.*/
- /**/
- /*Here we generate the dungeon randomly, with a rather simple algorithm.*/
- /*Most real games use pre-designed levels, preferrably stored in resources.*/
-
-
- /*Dungeon generation algorithm:*/
- /*We select a number of rooms to be placed, 3-10*/
- /*Each room is randomly assigned a size, and then a position.*/
- /*In each room we may put monsters and/or treasures.*/
- /*From each room except the last, we make a path from the room to the next.*/
- /*This guarantees that all rooms are connected.*/
-
-
- /*First fill the entire dungeon with walls!*/
- for ( h = 0 ; h < kArraySizeH ; h++)
- for ( v = 0 ; v < kArraySizeV ; v++)
- tileArray[h][v] = wall;
-
- /*All tiles are unknown when we start*/
- /*This must be in the beginning now, since MakeEntity draws*/
- /*the new entity*/
- for ( h = 0 ; h < kArraySizeH ; h++)
- for ( v = 0 ; v < kArraySizeV ; v++)
- tileKnown[h][v] = false;
-
- /*Dispose all old entities*/
- while (gEntityList != nil) DisposeEntity(gEntityList);
-
- /*Fill the entityArray with nil - there's nobody there!*/
- for ( h = 0 ; h < kArraySizeH ; h++)
- for ( v = 0 ; v < kArraySizeV ; v++)
- entityArray[h][v] = nil;
-
- numRooms = 3 + Rand(4); /*3 to 6 rooms*/
-
- /*Create each room*/
- for ( room = 0 ; room <= numRooms - 1 ; room++)
- {
- height = 1 + Rand(5 - numRooms / 2);
- width = 1 + Rand(5 - numRooms / 2);
- roomRect[room].top = 1 + Rand(kArraySizeV - height - 2);
- roomRect[room].bottom = roomRect[room].top + height;
- roomRect[room].left = 1 + Rand(kArraySizeH - width - 2);
- roomRect[room].right = roomRect[room].left + width;
-
- for ( h = roomRect[room].left ; h <= roomRect[room].right ; h++)
- for ( v = roomRect[room].top ; v <= roomRect[room].bottom ; v++)
- tileArray[h][v] = empty;
- };
-
- /*Make paths between all rooms*/
- for ( room = 0 ; room <= numRooms - 2 ; room++)
- {
- /*We make a path from h1, h2, to h2, v2*/
- h1 = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
- v1 = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
- h2 = roomRect[room + 1].left + Rand(roomRect[room + 1].right - roomRect[room + 1].left + 1);
- v2 = roomRect[room + 1].top + Rand(roomRect[room + 1].bottom - roomRect[room + 1].top + 1);
-
- /*First move along the h axis*/
- if ( h1 < h2 )
- for ( h = h1 ; h <= h2 ; h++)
- tileArray[h][v1] = empty;
- else
- for ( h = h1 ; h >= h2 ; h--)
- tileArray[h][v1] = empty;
- /*And then along the v axis*/
- if ( v1 < v2 )
- for ( v = v1 ; v <= v2 ; v++)
- tileArray[h2][v] = empty;
- else
- for ( v = v1 ; v >= v2 ; v--)
- tileArray[h2][v] = empty;
- };
-
- /*Now populate the rooms!*/
- for ( room = 1 ; room <= numRooms - 1 ; room++)
- {
- numTreasures = Rand(3); /*0 to 2 treasures*/
- for ( i = 1 ; i <= numTreasures ; i++)
- {
- h = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
- v = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
- if (entityArray[h][v] == nil)
- MakeEntity(goldEntity, h, v, Rand(100) + 1);
- // tileArray[h][v] = gold;
- };
- numMonsters = Rand(2); /*0 to 1 monsters*/
- for ( i = 1 ; i <= numMonsters; i++)
- {
- h = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
- v = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
- if (entityArray[h][v] == nil)
- MakeEntity(enemyEntity, h, v, 2);
- // tileArray[h][v] = enemy;
- };
- };
-
- /*Finally, place the player in the first room and the exit in the last. Also check that the exit is*/
- /*not the same position as the player! (It can happen, since we don't mak sure that rooms don't*/
- /*overlap!)*/
-
- /*Player position:*/
- h = roomRect[0].left + Rand(roomRect[0].right - roomRect[0].left + 1);
- v = roomRect[0].top + Rand(roomRect[0].bottom - roomRect[0].top + 1);
- if (entityArray[h][v] != nil)
- KillEntity(entityArray[h][v]);
- player = MakeEntity(playerEntity, h, v, 5);
- // tileArray[h][v] = player;
- /*Exit position:*/
- h = roomRect[numRooms - 1].left + Rand(roomRect[numRooms - 1].right - roomRect[numRooms - 1].left + 1);
- v = roomRect[numRooms - 1].top + Rand(roomRect[numRooms - 1].bottom - roomRect[numRooms - 1].top + 1);
- /*But please don't overwrite the player with the exit!*/
- if ( h == player->position.h )
- if ( v == player->position.v )
- {
- /*Try another room:*/
- h = roomRect[numRooms - 2].left + Rand(roomRect[numRooms - 2].right - roomRect[numRooms - 2].left + 1);
- v = roomRect[numRooms - 2].top + Rand(roomRect[numRooms - 2].bottom - roomRect[numRooms - 2].top + 1);
-
- /*Still failure? Darn. Just take a space next to the player.*/
- if ( h == player->position.h )
- if ( v == player->position.v )
- if ( h < kArraySizeH )
- h = h + 1;
- else
- h = h - 1;
- };
- /*OK, that's enough. Set the exit!*/
- tileArray[h][v] = exitPos;
-
- /*All tiles are unknown when we start*/
- /*…except the ones around the player*/
-
- /*Make the spaces around the player known*/
- ShowAroundPlayer();
-
- /*Draw all tiles!*/
- for ( h = 0 ; h < kArraySizeH ; h++)
- for ( v = 0 ; v < kArraySizeV ; v++)
- DrawTile(h, v);
- } /*CreateLevel*/
-
-
- /* Move an enemy */
-
- static void MoveEnemy(GameEntityPtr theEnemy /*short h, short v*/)
- {
- short dist;
- short newh, newv;
- short dir;
-
- /*1: decide if we are close to enough to the player to "hear" the player*/
-
- dist = abs(theEnemy->position.h - player->position.h) + abs(theEnemy->position.v - player->position.v); /*City Block distance*/
-
- /*2: Make a suggested destination, newh, newv*/
-
- if ( dist < Rand(15) ) /*Move towards the player*/
- {
- if ( theEnemy->position.h < player->position.h )
- newh = theEnemy->position.h + 1;
- else if ( theEnemy->position.h > player->position.h )
- newh = theEnemy->position.h - 1;
- else
- newh = theEnemy->position.h;
- if ( theEnemy->position.v < player->position.v )
- newv = theEnemy->position.v + 1;
- else if ( theEnemy->position.v > player->position.v )
- newv = theEnemy->position.v - 1;
- else
- newv = theEnemy->position.v;
- }
- else /*Move randomly*/
- {
- dir = Rand(8);
- newh = theEnemy->position.h + directionTable[dir].h;
- newv = theEnemy->position.v + directionTable[dir].v;
- }
-
- /*3: Check what is in the destination and take appropriate action (move, fight)*/
-
- switch ( tileArray[newh][newv] )
- {
- case empty:
- case exitPos:
- /* tileArray[newh][newv] = tempEnemy; /*We can't use "enemy", since then we might process it again in the same move!*/
- /* tileArray[h][v] = empty;*/
- if (entityArray[newh][newv] == player)
- {
- if ( Rand(10) > 5 ) /*Does it hit?*/
- { /*Enemy hits player!*/
- playerHitPoints--; /*Reduce player hit points*/
- DrawScore(); /*Draw score to display the lower hit points!*/
- if ( playerHitPoints > 0 )
- { /*Player still lives!*/
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer hit"), false) )
- ;
- }
- else
- { /*Player died!*/
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer died"), false) )
- ;
- };
- }
- else
- { /*Miss!*/
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer miss"), false) )
- ;
- }
- }
- else
- if (entityArray[newh][newv] == nil)
- {
- entityArray[theEnemy->position.h][theEnemy->position.v] = nil;
- entityArray[newh][newv] = theEnemy;
- DrawTile(newh, newv);
- DrawTile(theEnemy->position.h, theEnemy->position.v);
- theEnemy->position.h = newh;
- theEnemy->position.v = newv;
- }
- break;
-
- case wall:
- // case enemy:
- // case gold:
- // case exitPos:
- // case tempEnemy:
- ; /*Don't move to any of these!*/
- }; /*case*/
- } /*MoveEnemy*/
-
-
- /* Initialize - create window, load graphics */
-
- static void InitDungeon()
- {
- Rect windowRectangle;
-
- /*Set up the window*/
- SetRect(&windowRectangle, 50, 50, 50 + kArraySizeH * kTileSizeH, 50 + kArraySizeV * kTileSizeV + kScoreFieldHeight);
- myWindow = NewCWindow(nil, &windowRectangle, "\pDungeon E", true, documentProc, (WindowPtr)-1L, false, 0);
- SetPort(myWindow);
-
- qd.randSeed = TickCount (); /*Seed the random number generator*/
-
- /*Load all pictures*/
- floorTile = GetPicture(128); /*PICT resource #128.*/
- playerTile = GetPicture(129); /*PICT resource #129.*/
- enemyTile = GetPicture(130); /*PICT resource #130.*/
- goldTile = GetPicture(131); /*PICT resource #131.*/
- wallTile = GetPicture(132); /*PICT resource #132.*/
- exitTile = GetPicture(133); /*PICT resource #133.*/
- } /*InitDungeon*/
-
-
- /* Set up for a new game */
-
- static void NewGame()
- {
- /* Fill in the tileArr array */
- CreateLevel();
-
- /* Start with a healthy player */
- playerHitPoints = 5;
- gGold = 0;
- gExperience = 0;
- } /*NewGame*/
-
-
- /* ValidMove checks if a tile clickedTile is inside the array bounds *and* near the player */
-
- static Boolean ValidMove(Point clickedTile)
- {
- /* Valid tile?*/
- if ( clickedTile.h >= 0 )
- if ( clickedTile.v >= 0 )
- if ( clickedTile.h < kArraySizeH )
- if ( clickedTile.v < kArraySizeV )
- /* OK, we are inside the game area, clicking in some space! Is it next to the player?*/
- if ( clickedTile.h >= player->position.h - 1 )
- if ( clickedTile.h <= player->position.h + 1 )
- if ( clickedTile.v >= player->position.v - 1 )
- if ( clickedTile.v <= player->position.v + 1 )
- return true;
- /* If that wasn't the case, it wasn't vcalid; return false! */
- return false;
- } /*ValidMove*/
-
-
- /* Try to move the player to the position where we clicked. */
-
- static void MovePlayer(Point clickedTile)
- {
- short h, v;
- GameEntityPtr theEntity;
-
- /* Valid move?*/
- if (ValidMove(clickedTile))
- /* Yes! What is there? */
- {
- if (entityArray[clickedTile.h][clickedTile.v] != nil)
- {
- switch (entityArray[clickedTile.h][clickedTile.v]->kind)
- {
- case enemyEntity:
- if ( Rand(10) > 4 )
- { /*Hit! Play a "monster died" sound*/
-
- entityArray[clickedTile.h][clickedTile.v]->value--;
- if (entityArray[clickedTile.h][clickedTile.v]->value <= 0)
- { /* Monster died! */
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pEnemy died"), false) )
- ;
- /* Walk to the space where the monster was.*/
- /*tileArray[player->position.h][player->position.v] = empty;
- tileArray[clickedTile.h][clickedTile.v] = player;*/
-
- KillEntity(entityArray[clickedTile.h][clickedTile.v]);
- entityArray[player->position.h][player->position.v] = nil;
- entityArray[clickedTile.h][clickedTile.v] = player;
-
- DrawTile(player->position.h, player->position.v);
- DrawTile(clickedTile.h, clickedTile.v);
- player->position = clickedTile;
-
- /* Killed a monster! Get experience! Let's say one point for the troll! */
- AddScore(0, 1);
- }
- else /* Monster still lives! */
- {
- }
- }
- else
- { /*Miss! Play the "miss" sound. */
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pEnemy miss"), false) )
- ;
- }
- break;
- case goldEntity:
- /* Gold - add score! */
- AddScore(entityArray[clickedTile.h][clickedTile.v]->value, 0);
-
- KillEntity(entityArray[clickedTile.h][clickedTile.v]);
- entityArray[player->position.h][player->position.v] = nil;
- entityArray[clickedTile.h][clickedTile.v] = player;
-
- DrawTile(player->position.h, player->position.v);
- DrawTile(clickedTile.h, clickedTile.v);
- player->position = clickedTile;
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pMoney"), false) )
- ;
- break;
- } /* case kind */
- }
- else
- switch ( tileArray[clickedTile.h][clickedTile.v] )
- {
- case empty:
- entityArray[player->position.h][player->position.v] = nil;
- entityArray[clickedTile.h][clickedTile.v] = player;
-
- DrawTile(player->position.h, player->position.v);
- DrawTile(clickedTile.h, clickedTile.v);
- player->position = clickedTile;
- break;
- case wall:
- SysBeep(1);
- break;
- case exitPos:
- entityArray[player->position.h][player->position.v] = nil;
- entityArray[clickedTile.h][clickedTile.v] = player;
-
- DrawTile(player->position.h, player->position.v);
- DrawTile(clickedTile.h, clickedTile.v);
- player->position = clickedTile;
- if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pNext level"), false) )
- ;
- CreateLevel(); /* Don't quit - make a new level instead */
- break;
- }; /*case*/
- }; /*valid move*/
-
- ShowAroundPlayer();
-
- /* Monsters are allowed to move now!*/
-
- theEntity = gEntityList;
- while (theEntity != nil)
- {
- if (theEntity->kind == enemyEntity)
- MoveEnemy(theEntity);
- theEntity = theEntity->next;
- }
-
- } /*MovePlayer*/
-
-
- /* Handle mouse downs */
-
- static void DoMouse(Point clickPoint, long mods)
- {
- Point clickedTile;
- short h, v;
-
- /* If the hero is dead, we can't move! */
- if ( playerHitPoints < 1 )
- {
- SysBeep(1);
- return;
- };
-
- /*Convert clickPoint to local coordinates*/
- GlobalToLocal(&clickPoint);
- /*Convert it to coordinates in the arrays.*/
- clickedTile.h = clickPoint.h / kTileSizeH;
- clickedTile.v = clickPoint.v / kTileSizeV;
- MovePlayer(clickedTile);
- } /*DoMouse*/
-
-
- /* Empty stub for a background task. */
-
- static void DoBackground()
- {
- } /*DoBackground*/
-
-
- /* Add two points - simplifies the keydown handler */
-
- static Point AddPoints(Point p1,Point p2)
- {
- Point dest;
-
- dest.h = p1.h + p2.h;
- dest.v = p1.v + p2.v;
- return dest;
- }; /*AddPoints*/
-
-
- /* Handle key downs */
-
- static void DoKey(char theKey, long mods)
- {
- /* If the hero is dead, we can't move! */
- if ( playerHitPoints < 1 )
- {
- SysBeep(1);
- return;
- };
-
- /* For now, we use a hard-coded key mapping. */
- switch ( theKey )
- {
- case 'd':
- case '6':
- MovePlayer(AddPoints(player->position, directionTable[0]));break;
- case 'e':
- case '9':
- MovePlayer(AddPoints(player->position, directionTable[1]));break;
- case 'w':
- case '8':
- MovePlayer(AddPoints(player->position, directionTable[2]));break;
- case 'q':
- case '7':
- MovePlayer(AddPoints(player->position, directionTable[3]));break;
- case 'a':
- case '4':
- MovePlayer(AddPoints(player->position, directionTable[4]));break;
- case 'z':
- case '1':
- MovePlayer(AddPoints(player->position, directionTable[5]));break;
- case 'x':
- case '2':
- MovePlayer(AddPoints(player->position, directionTable[6]));break;
- case 'c':
- case '3':
- MovePlayer(AddPoints(player->position, directionTable[7]));break;
- default:
- SysBeep(1);
- }; /*case*/
- }; /*DoKey*/
-
-
- /* Handle selections in the File menu */
-
- static void DoFileMenu(short item)
- {
- switch ( item )
- {
- case 1:
- NewGame();break;
- case 3:
- gDone = true;break;
- }; /*case*/
- }; /*DoFileMenu*/
-
-
- /* The "About" item was selected */
-
- static void DoAbout()
- {
- short ignore;
-
- ignore = Alert(128, nil);
- }; /*DoAbout*/
-
-
- /* Handle update events */
-
- static void DoUpdate()
- {
- short h, v;
-
- BeginUpdate(myWindow);
- /*Draw all tiles!*/
- for ( h = 0 ; h < kArraySizeH ; h++)
- for ( v = 0 ; v < kArraySizeV ; v++)
- DrawTile(h, v);
-
- DrawScore();
-
- EndUpdate(myWindow);
- }; /*DoUpdate*/
-
-
- /* Handle menu selections */
-
- static void DoMenuSelection(long mSelect)
- {
- short menuID;
- short menuItem;
- GrafPtr savePort;
- Str255 name;
- short ignore;
-
- menuID = HiWord(mSelect);
- menuItem = LoWord(mSelect);
-
- switch ( menuID )
- {
- case appleID:
- if ( menuItem == 1 )
- DoAbout();
- else
- {
- GetPort(&savePort);
- GetItem(GetMHandle(appleID), menuItem, name);
- ignore = OpenDeskAcc(name);
- SetPort(savePort);
- };
- break;
- case fileID:
- DoFileMenu(menuItem);break;
- default:
- ;}; /*case*/
- HiliteMenu(0);
- }; /*DoMenuSelection*/
-
-
- /* Set up menus */
-
- static void SetupMenus()
- {
- MenuHandle appleMenu, fileMenu;
- Str255 tempStr;
-
- tempStr[0]=1; tempStr[1] = (char)20;
- appleMenu = NewMenu(appleID, tempStr); /*Apple menu symbol*/
- InsertMenu(appleMenu, 0);
- AppendMenu(appleMenu, "\pAbout Dungeon…;(-");
- AppendResMenu(appleMenu, 'DRVR');
-
- fileMenu = GetMenu(128);
- InsertMenu(fileMenu, 0);
- DrawMenuBar ();
-
- }; /*SetupMenus*/
-
-
- /* Main event loop */
-
- static void MainLoop(void)
- {
- #define kSleep 5 /*Real programs may modify the sleep time depending on whether or not they are in the front*/
-
- EventRecord theEvent;
- char theKey;
- long whatSelection;
- short whichPart;
- WindowPtr whichWindow;
- Rect r;
- Point diskInitPt = {40,40};
-
- /*Get the next event with WaitNextEvent.*/
- if ( WaitNextEvent(everyEvent, &theEvent, kSleep, nil) )
- switch ( theEvent.what )
- {
- case mouseDown:
- /*We must find out what kind of mouse down this was.*/
- whichPart = FindWindow(theEvent.where, &whichWindow);
- switch ( whichPart )
- {
- case inMenuBar:
- /*Click in menu bar. Let the system call MenuSelect track it.*/
- whatSelection = MenuSelect(theEvent.where);
- DoMenuSelection(whatSelection); /*Our own routine for handling menu selections*/
- break;
- case inSysWindow:
- /*Click in some window that isn't ours*/
- SystemClick(&theEvent, whichWindow);
- break;
- case inGoAway:
- /*Click in close box of window. For "desk accessory"-style games, that is a good quit signal*/
- if ( (TrackGoAway(whichWindow, theEvent.where)) )
- gDone = true;
- break;
- case inDrag:
- /*Drag a window*/
- if ( (whichWindow != FrontWindow ()) && ((theEvent.modifiers, cmdKey) == 0) )
- SelectWindow(whichWindow);
- DragWindow(whichWindow, theEvent.where, &qd.screenBits.bounds);
- break;
- case inGrow:
- ; /*Ignored - we don't resize*/
- break;
- case inContent:
- /*Click in the window.*/
- if ( (whichWindow != FrontWindow ()) )
- SelectWindow(whichWindow);
- else
- DoMouse(theEvent.where, theEvent.modifiers); /*Go to application-specific mouse down handling*/
- }; /*case whichPart*/
- break; /*mouseDown*/
- case keyDown:
- case autoKey:
- /*If the command key is pressed, it is a menu selection*/
- theKey = (char)(theEvent.message & charCodeMask);
- if ( ((theEvent.modifiers & cmdKey) != 0) )
- DoMenuSelection(MenuKey(theKey)); /*Our own routine for handling menu selections*/
- else
- /*Otherwise, it's a normal key down.*/
- DoKey(theKey, theEvent.modifiers);
- break;
- case updateEvt:
- /*Find out what event the update event is for.*/
- if ( (WindowPtr)theEvent.message == myWindow )
- DoUpdate();
- break;
- case diskEvt:
- /*Handle bad disk insertions*/
- if (HiWord(theEvent.message) != noErr)
- {
- DILoad();
- DIBadMount(diskInitPt, theEvent.message);
- DIUnload();
- }
- break;
- default: /*Other events are ignored*/
- ;}; /*case*/
-
- /*For each turn to the event loop, we may to call some "background" process, e.g. animations.*/
- DoBackground();
- } /*MainLoop*/
-
-
- /* Standard inits */
-
- static void InitToolbox(void) {
- InitGraf (&qd.thePort);
- InitFonts ();
- FlushEvents (everyEvent,0);
- InitWindows ();
- InitMenus ();
- TEInit ();
- InitDialogs (nil);
- InitCursor ();
- }
-
-
- /* Main program */
-
- void main(void)
- {
- InitToolbox();
- InitDungeon();
- SetupMenus();
- NewGame();
- /*Initializations done! Run the game loop until the game ends.*/
- do
- {
- MainLoop();
- } while (! gDone);
- } /*Dungeon*/
-
-
- /*What's left for making a real game of it?*/
- /*- Animations*/
- /*- Several levels*/
- /*- Faster drawing*/
- /*- Asynch sound*/
- /*- More objects, i.e. weapons, monsters, treasures…*/